// ArrayAutoPtr.h
// (c) 2008, Charles Lechasseur
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#ifndef RRO_UTILS_MEMORY_ARRAYAUTOPTR_H
#define RRO_UTILS_MEMORY_ARRAYAUTOPTR_H

#pragma once


namespace RRO
{
    //
    // Smart pointer to store an array of elements. Behaves like std::auto_ptr but
    // uses delete [] to get rid of the array in the destructor.
    //
    // The method names have been made similar to std::auto_ptr to make it more interchangeable.
    //
    template<class T>
    class ArrayAutoPtr
    {
    public:
        typedef T           element_type;

                            //
                            // Creates a smart pointer saving the given array.
                            //
                            // @param p_pArray Array to store.
                            //
        explicit            ArrayAutoPtr(T* p_pArray = 0)
                                : m_pArray(p_pArray)
                            {
                            }

                            //
                            // Copies the array stored in another smart pointer to this one.
                            // Note that the other smart pointer will no longer point to the array.
                            //
                            // @param p_rAutoPtr Smart pointer to copy the array from.
                            //
                            ArrayAutoPtr(ArrayAutoPtr<T>& p_rAutoPtr)
                                : m_pArray(p_rAutoPtr.release())
                            {
                            }

                            //
                            // Copies the array stored in another smart pointer to this one.
                            // This templated version works as long as an implicit conversion
                            // between U and our element_type is possible.
                            // Note that the other smart pointer will no longer point to the array.
                            //
                            // @param p_rAutoPtr Smart pointer to copy the array from.
                            //
        template<class U>   ArrayAutoPtr(ArrayAutoPtr<U>& p_rAutoPtr)
                                : m_pArray(p_rAutoPtr.release())
                            {
                            }

                            //
                            // Assigns the array pointed to by another smart pointer to this one.
                            // Note that the other smart pointer will no longer point to the array.
                            //
                            // @param p_rAutoPtr Smart pointer to copy the array from.
                            //
        ArrayAutoPtr<T>&    ArrayAutoPtr::operator=(ArrayAutoPtr<T>& p_rAutoPtr)
                            {
                                if (this != &p_rAutoPtr) {
                                    m_pArray = p_rAutoPtr.release();
                                }
                                return *this;
                            }

                            //
                            // Assigns the array pointed to by another smart pointer to this one.
                            // This templated version works as long as an implicit conversion
                            // between U and our element_type is possible.
                            // Note that the other smart pointer will no longer point to the array.
                            //
                            // @param p_rAutoPtr Smart pointer to copy the array from.
                            //
        template<class U>
        ArrayAutoPtr<T>&    ArrayAutoPtr::operator=(ArrayAutoPtr<U>& p_rAutoPtr)
                            {
                                // Self assignment is impossible here.
                                m_pArray = p_rAutoPtr.release();
                            }

                            //
                            // Destructor. Deletes the stored array, if any.
                            //
                            ~ArrayAutoPtr(void)
                            {
                                reset();
                            }

                            //
                            // Returns the stored array. Can be null.
                            //
        T*                  get(void) const
                            {
                                return m_pArray;
                            }

                            //
                            // Clears the array referenced by this smart pointer and returns the orphaned array.
                            //
        T*                  release(void)
                            {
                                T* pArray = m_pArray;
                                m_pArray = 0;
                                return pArray;
                            }

                            //
                            // Resets the smart pointer to point to a new array.
                            // Any existing array is destroyed.
                            //
                            // @param p_pArray New array pointer. Can be null.
                            //
        void                reset(T* p_pArray = 0)
                            {
                                if (p_pArray != m_pArray) {
                                    if (m_pArray != 0) {
                                        delete [] m_pArray;
                                    }
                                    m_pArray = p_pArray;
                                }
                            }

                            //
                            // Returns reference to array stored in this smart pointer.
                            // This will effectively return a reference to the first element.
                            //
        T&                  operator*(void) const
                            {
                                return *get();
                            }

                            //
                            // Returns pointer to array stored in this smart pointer.
                            // This effectively allows caller to access the first element stored in the array.
                            //
        T*                  operator->(void) const
                            {
                                return get();
                            }

                            //
                            // Returns reference to the nth element stored in the array of this smart pointer.
                            //
                            // @param p_Index Index of element. Bounds are not checked; caller is responsible
                            //                for passing a valid index.
                            //
        T&                  operator[](int p_Index) const
                            {
                                return get()[p_Index];
                            }

                            //
                            // Casts this auto-pointer into another.
                            // This works as long as an implicit conversion between U and
                            // our element_type is possible.
                            //
        template<class U>   operator ArrayAutoPtr<U>(void) const
                            {
                                return ArrayAutoPtr<U>(*this);
                            }

    private:
        // Saved array pointer.
        T*                  m_pArray;
    };

}; // namespace RRO


#endif RRO_UTILS_MEMORY_ARRAYAUTOPTR_H
